package db

import (
	"core/mysql"
	"core/rds"
	"core/utils"
	"encoding/json"
	"fmt"
	"time"
)

type (
	PicUploadModel       struct{}
	PicUploadAccessModel struct{}
	PicUploadItem        struct {
		ID       int64  `json:"id"`        // 图片唯一ID，目前根据时间戳，只要保证自己唯一即可
		Name     string `json:"name"`      // 图片名
		CreateAt int64  `json:"create_at"` // 创建时间戳
		Status   int    `json:"status"`    // 状态
		ConfId   int64  `json:"conf_id"`   // 配置ID，只有在采纳之后才会有
	}

	PicUpload struct {
		ID   int64           `xorm:"pk autoincr id"` //
		List []PicUploadItem `xorm:"TEXT"`           // 审核通过图片列表
	}
	// 上传图片 mysql存储方式
	PicUploadMysql struct {
		ID   int64  `xorm:"pk autoincr id"` //
		Data string `xorm:"TEXT"`           //
	}

	PicUploadAccess struct {
		ID   int64           `xorm:"pk autoincr id"` //
		List []PicUploadItem `xorm:"TEXT"`           // 审核通过图片列表
	}
	// 上传图片 mysql存储方式
	PicUploadAccessMysql struct {
		ID   int64  `xorm:"pk autoincr id"` //
		Data string `xorm:"TEXT"`           //
	}
)

const (
	PicUploadModelKey       = "pic_upload_model"
	PicUploadModelAccessKey = "pic_upload_model_access"
	PicUploadExpireSecond   = time.Hour * 24 * 3

	PicUploadLenLimit = 5

	PicUploadStatusInit   = 0 // 初始状态
	PicUploadStatusAccess = 1 // 已采纳
)

func init() {
	RegisterModel(PicUploadModelKey, PicUploadModel{})
	RegisterModel(PicUploadModelAccessKey, PicUploadAccessModel{})
}

// 实现接口---------------------------------------------------------------------------------------------

func (PicUpload) TableName() string {
	return "pic_upload_mysql"
}

func (m PicUploadModel) GetTable() (string, interface{}) {
	return PicUpload{}.TableName(), &PicUploadMysql{}
}

func (m PicUploadModel) SaveToMysql(uid int64) (err error) {
	utils.Info("PicUploadModel,保存数据到mysql", uid)
	_, _ = m.SyncIfNotExist(uid, nil)
	pic := LoadPicUploadFromRedis(uid)
	data, err := json.Marshal(pic)
	if err != nil {
		utils.Error(err)
		return
	}
	picMysql := &PicUploadMysql{
		ID:   uid,
		Data: string(data),
	}
	err = mysql.Save(uid, picMysql)
	if err != nil {
		utils.Error(err)
		return
	}
	return
}

// 如果redis中数据过期了，则把数据同步过来
// 如果data不为空，同步redis中部分变化字段到mysql
func (m PicUploadModel) SyncIfNotExist(uid int64, data interface{}) (exist bool, err error) {
	client := rds.GetPicUploadClient(uid)
	key := MakePicUploadKey(uid)
	exist = client.Exists(key).Val() > 0
	if exist {
		return
	}
	picMysql := &PicUploadMysql{ID: uid}
	err = mysql.Load(picMysql)
	if err != nil {
		utils.Error(err)
		return
	}
	pic := new(PicUpload)
	err = json.Unmarshal([]byte(picMysql.Data), pic)
	if err != nil {
		utils.Error(err)
		return
	}

	if len(pic.List) > 0 {
		contents := make([]interface{}, 0)
		for _, item := range pic.List {
			if item.ID == 0 {
				continue
			}
			content, _ := json.Marshal(item)
			contents = append(contents, string(content))
		}

		_, err = client.RPush(key, contents...).Result()
		if err != nil {
			utils.Error(err)
			return
		}
		err = client.Expire(key, PicUploadExpireSecond).Err()
		if err != nil {
			utils.Error(err)
			return
		}
	}

	// 如果传进来的data为空，则不赋值， 只同步
	if data != nil {
		err = json.Unmarshal([]byte(picMysql.Data), data)
		if err != nil {
			utils.Error(err)
		}
	}
	return
}

func MakePicUploadKey(uid int64) string {
	return fmt.Sprintf("pic-upload:%d", uid)
}

func LoadPicUploadIfNotExist(uid int64) (pic *PicUpload, err error) {
	pic = &PicUpload{ID: uid}
	exist, err := PicUploadModel{}.SyncIfNotExist(uid, pic)
	if err == mysql.NotFound {
		err = nil
		return
	}
	if err != nil || !exist { // 出错 或 从mysql中已获取数据
		return
	}

	pic = LoadPicUploadFromRedis(uid)
	return
}

func LoadPicUploadFromRedis(uid int64) (pic *PicUpload) {
	pic = &PicUpload{ID: uid}
	client := rds.GetPicUploadClient(uid)
	key := MakePicUploadKey(uid)
	list := client.LRange(key, 0, -1).Val()
	pic.List = make([]PicUploadItem, len(list))
	for k, content := range list {
		_ = json.Unmarshal([]byte(content), &pic.List[k])
	}
	return
}

// 增加上传的图片 redis即时操作，mysql异步同步
func AddPicUpload(uid int64, items ...PicUploadItem) (err error) {
	if len(items) == 0 {
		return
	}
	_, err = PicUploadModel{}.SyncIfNotExist(uid, nil)
	if err == mysql.NotFound {
		err = nil
	} else if err != nil {
		return
	}

	client := rds.GetPicUploadClient(uid)
	key := MakePicUploadKey(uid)
	contents := make([]interface{}, len(items))
	for k, item := range items {
		content, _ := json.Marshal(item)
		contents[k] = string(content)
	}

	length, err := client.LPush(key, contents...).Result()
	if err != nil {
		return
	}
	if length >= PicUploadLenLimit*2 {
		client.LTrim(key, 0, PicUploadLenLimit)
	}
	err = client.Expire(key, PicUploadExpireSecond).Err()
	if err != nil {
		utils.Error(err)
		return
	}

	// 放入mysql同步列表
	err = PushToSyncList(&ModelData{Key: PicUploadModelKey, UID: uid})
	if err != nil {
		utils.Error(err)
		return
	}
	return
}

// 删除我上传的图片 redis和mysql都是同步操作
func DelPicUpload(uid, uploadId int64) (err error) {
	utils.Info("DelPicUpload", uid, uploadId)
	// 获取pic
	_, _ = PicUploadModel{}.SyncIfNotExist(uid, nil)
	pic := LoadPicUploadFromRedis(uid)

	// 从redis中删除
	idx := -1
	newList := make([]PicUploadItem, 0)
	for k, item := range pic.List {
		if item.ID == uploadId {
			idx = k
		} else {
			newList = append(newList, item)
		}
	}
	if idx < 0 {
		utils.Error("idx < 0")
		return
	}
	client := rds.GetPicUploadClient(uid)
	key := MakePicUploadKey(uid)
	item := pic.List[idx]
	content, _ := json.Marshal(item)
	err = client.LRem(key, 1, string(content)).Err()
	if err != nil {
		utils.Error(err)
		return
	}
	// 从mysql删除
	utils.Info("从mysql删除", newList)
	pic.List = newList
	data, err := json.Marshal(pic)
	if err != nil {
		utils.Error(err)
		return
	}
	picMysql := &PicUploadMysql{
		ID:   uid,
		Data: string(data),
	}
	err = mysql.Save(uid, picMysql)
	if err != nil {
		utils.Error(err)
		return
	}
	return
}

// ---------------------------------------------------------------------------------------------------

func (PicUploadAccess) TableName() string {
	return "pic_upload_access_mysql"
}

func (m PicUploadAccessModel) SaveToMysql(uid int64) (err error) {
	utils.Info("PicUploadAccessModel,保存数据到mysql", uid)
	_, _ = m.SyncIfNotExist(uid, nil)
	pic, err := LoadPicUploadAccessFromRedis(uid)
	if err != nil {
		utils.Error(err)
		return
	}
	data, err := json.Marshal(pic)
	if err != nil {
		utils.Error(err)
		return
	}
	picMysql := &PicUploadAccessMysql{
		ID:   uid,
		Data: string(data),
	}
	err = mysql.Save(uid, picMysql)
	if err != nil {
		utils.Error(err)
	}
	return
}

func (m PicUploadAccessModel) GetTable() (string, interface{}) {
	return PicUploadAccess{}.TableName(), &PicUploadAccessMysql{}
}

func (m PicUploadAccessModel) SyncIfNotExist(uid int64, data interface{}) (exist bool, err error) {
	client := rds.GetPicUploadClient(uid)
	key := MakePicUploadAccessKey(uid)
	exist = client.Exists(key).Val() > 0
	if exist {
		return
	}
	picMysql := &PicUploadAccessMysql{ID: uid}
	err = mysql.Load(picMysql)
	if err != nil {
		utils.Error(err)
		return
	}
	pic := new(PicUpload)
	err = json.Unmarshal([]byte(picMysql.Data), pic)
	if err != nil {
		utils.Error(err)
		return
	}

	contents := make([]interface{}, 0)
	for _, item := range pic.List {
		if item.ID == 0 {
			continue
		}
		content, _ := json.Marshal(item)
		contents = append(contents, string(content))
	}
	if len(contents) > 0 {
		_, err = client.RPush(key, contents...).Result()
		if err != nil {
			utils.Error(err)
			return
		}
		err = client.Expire(key, PicUploadExpireSecond).Err()
		if err != nil {
			utils.Error(err)
			return
		}
	}

	// 如果传进来的data为空，则不赋值， 只同步
	if data != nil {
		err = json.Unmarshal([]byte(picMysql.Data), data)
		if err != nil {
			utils.Error(err)
		}
	}
	return
}

func MakePicUploadAccessKey(uid int64) string {
	return fmt.Sprintf("pic-upload:%d-access", uid)
}

func LoadPicUploadAccessIfNotExist(uid int64) (pic *PicUploadAccess, err error) {
	pic = &PicUploadAccess{ID: uid}
	exist, err := PicUploadAccessModel{}.SyncIfNotExist(uid, pic)
	utils.Info("redis中已存在吗:", exist)
	if err == mysql.NotFound {
		err = nil
		return
	}
	if err != nil || !exist { // 出错 或 从mysql中已获取数据
		return
	}
	pic, err = LoadPicUploadAccessFromRedis(uid)
	if err != nil {
		utils.Error(err)
	}
	return
}

func LoadPicUploadAccessFromRedis(uid int64) (pic *PicUploadAccess, err error) {
	pic = &PicUploadAccess{ID: uid}
	client := rds.GetPicUploadClient(uid)
	key := MakePicUploadAccessKey(uid)
	list := client.LRange(key, 0, -1).Val()
	pic.List = make([]PicUploadItem, len(list))
	for k, content := range list {
		err = json.Unmarshal([]byte(content), &pic.List[k])
		if err != nil {
			utils.Error(err, content)
		}
	}
	return
}

// 删除我上传成功的图片
func DelPicUploadAccess(uid, uploadId int64) (err error) {
	utils.Info("DelPicUploadAccess", uid, uploadId)

	// 获取pic
	pic, err := LoadPicUploadAccessIfNotExist(uid)
	if err != nil {
		utils.Error(err)
		return
	}
	// 从redis中删除
	idx := -1
	newList := make([]PicUploadItem, 0)
	for k, item := range pic.List {
		if item.ID == uploadId {
			idx = k
		} else {
			newList = append(newList, item)
		}
	}
	if idx < 0 {
		utils.Error("idx < 0")
		return
	}
	client := rds.GetPicUploadClient(uid)
	key := MakePicUploadAccessKey(uid)
	item := pic.List[idx]
	content, _ := json.Marshal(item)
	err = client.LRem(key, 1, string(content)).Err()
	if err != nil {
		utils.Error(err)
		return
	}

	// 从mysql中删除
	pic.List = newList
	data, err := json.Marshal(pic)
	if err != nil {
		utils.Error(err)
		return
	}
	picMysql := &PicUploadAccessMysql{
		ID:   uid,
		Data: string(data),
	}
	err = mysql.Save(uid, picMysql)
	if err != nil {
		utils.Error(err)
		return
	}
	return
}

func AddPicUploadAccess(uid int64, items ...PicUploadItem) (err error) {
	if len(items) == 0 {
		return
	}
	_, err = PicUploadAccessModel{}.SyncIfNotExist(uid, nil)
	if err == mysql.NotFound {
		err = nil
	} else if err != nil {
		utils.Error(err)
		return
	}

	client := rds.GetPicUploadClient(uid)
	key := MakePicUploadAccessKey(uid)
	contents := make([]interface{}, 0)
	for _, item := range items {
		content, _ := json.Marshal(item)
		contents = append(contents, string(content))
	}

	length, err := client.LPush(key, contents...).Result()
	if err != nil {
		utils.Error(err)
		return
	}
	if length >= PicUploadLenLimit*2 {
		client.LTrim(key, 0, PicUploadLenLimit)
	}
	err = client.Expire(key, PicUploadExpireSecond).Err()
	if err != nil {
		utils.Error(err)
		return
	}

	err = PicUploadAccessModel{}.SaveToMysql(uid)
	if err != nil {
		utils.Error(err)
	}
	return
}

// ---------------------------------------------------------------------------------------------------

func SavePlayerPicStatus(online bool, pic *PicOnline) (err error) {
	if online {
		utils.Info("PicUploadAccessModel{}.AddPicUploadAccess")
		err = AddPicUploadAccess(pic.AuthorUID, PicUploadItem{
			ID:       pic.UploadId,
			Name:     pic.OriginName,
			CreateAt: pic.UploadAtUnix,
			Status:   PicUploadStatusAccess,
			ConfId:   pic.ID,
		})
		if err != nil {
			utils.Error(err)
			return
		}
		utils.Info("PicUploadModel{}.DelPicUpload")
		err1 := DelPicUpload(pic.AuthorUID, pic.UploadId)
		if err1 != nil {
			utils.Error(err1)
			return
		}
	} else {

		err = AddPicUpload(pic.AuthorUID, PicUploadItem{
			ID:       pic.UploadId,
			Name:     pic.OriginName,
			CreateAt: pic.UploadAtUnix,
			Status:   PicUploadStatusInit,
			ConfId:   pic.ID,
		})
		if err != nil {
			utils.Error(err)
			return
		}
		err1 := DelPicUploadAccess(pic.AuthorUID, pic.UploadId)
		if err1 != nil {
			utils.Error(err1)
			return
		}
	}

	return
}
